import os
import sys


def eprint(*args, **kwargs):
    """
    Вывод сообщения в stderr
    """
    print(*args, file=sys.stderr, **kwargs)


def read_ini_file(file):
    """
    Возвращает список строк файла конфига
    :param file: имя файла
    :return: список строк без пустых символов в конце
    """
    with open(file, 'r') as inp:
        lines = [line.rstrip() for line in inp.readlines()]
    return lines


def write_config_to_file(config, file):
    """
    Записывает конфиг в файл
    :param config: список строк конфига
    :param file: имя файла
    """
    with open(file, 'w') as out:
        print(*config, sep='\n', file=out)


def trim_empty_strings(file):
    """
    Удаляет пустые строки из файла
    :param file: имя файла
    """
    config = read_ini_file(file)
    index = 0
    while index < len(config) - 1:
        if config[index].strip() == '' and config[index + 1].strip() == '':
            config = config[:index] + config[index + 1:]
        else:
            index += 1
    while len(config) > 0 and config[0].strip() == '':
        config = config[1:]
    while len(config) > 0 and config[-1].strip() == '':
        config = config[:-1]
    write_config_to_file(config, file)


def get_comment_symbol(filename: str) -> str:
    """
    Просматривает файл и определяет символ, с которого начинаются комментарии
    :param filename: имя файла
    :return: символ, с которого начинаются комментарии (# или ;)
    """
    with open(filename) as inp:
        lines = inp.readlines()
        for line in lines:
            if line.strip().startswith(';'):
                return ';'
    return '#'


def get_all_files_to_recursive_scan(files_to_recursive_scan, required_extension=None, inversed_order=False,
                                    sort_collected=False, shadowing=False, verbose=False):
    """
    Получает список файлов. Возвращает этот же список, расширенный всеми файлами в папках,
    имена которых состоят из этих имён + '.d'
    :param verbose: вывод логов в stderr (True/False)
    :param shadowing: если указано, то файлы с одинаковыми именами, которые рассмотрены позднее,
    отменяют найденные значения в ранее рассмотренных, даже если таких переменных нет
    :param sort_collected: если указано, то все файлы, найденные в папках, сортируются по алфавиту по имени без пути,
    перед тем, как в них будут искать значения
    :param files_to_recursive_scan: исходный список файлов
    :param required_extension: требуемое расширение. Если задано, в папках будут
    рассматриваться только файлы с таким расширением
    :param inversed_order: если задано, сначала будут рассматриваться файлы в папках, только потом исходные
    :return: список абсолютных адресов файлов
    """
    files = []
    files_from_folders_paths_by_name = dict()
    if not sort_collected:
        for file in files_to_recursive_scan:
            # сначала добавляется файл, потом файлы из папки
            if not inversed_order and os.path.isfile(file):
                files.append(file)
                if verbose:
                    eprint(f'Добавлен файл {file}')
            folder = file + '.d'
            if not os.path.isdir(folder):
                if verbose:
                    eprint(f'Папка {folder} не найдена')
                continue
            for file_in_folder in sorted(os.listdir(folder)):
                if not shadowing:
                    files.append(os.path.join(folder, file_in_folder))
                else:
                    if file_in_folder not in files_from_folders_paths_by_name.keys():
                        files.append(os.path.join(folder, file_in_folder))
                        files_from_folders_paths_by_name[file_in_folder] = os.path.join(folder, file_in_folder)
                    else:
                        index = files.index(files_from_folders_paths_by_name[file_in_folder])
                        if verbose:
                            eprint(f'Файл {files[index]} замещён файлом {os.path.join(folder, file_in_folder)}')
                        files.remove(files[index])
                        files.append(os.path.join(folder, file_in_folder))
            if verbose:
                eprint(f'Добавлены файлы из папки {folder}')
            # сначала добавляются файлы из папки, потом файл
            if inversed_order and os.path.isfile(file):
                files.append(file)
                if verbose:
                    eprint(f'Добавлен файл {file}')

    else:
        files_from_folders = []
        for file in files_to_recursive_scan:
            folder = file + '.d'
            if not os.path.isdir(folder):
                if verbose:
                    eprint(f'Папка {folder} не найдена')
                continue
            for file_in_folder in sorted(os.listdir(folder)):
                if file_in_folder not in [i[0] for i in files_from_folders] or not shadowing:
                    files_from_folders.append([file_in_folder, os.path.join(folder, file_in_folder)])
                    if verbose:
                        eprint(f'Добавлен файл {os.path.join(folder, file_in_folder)}')
                else:
                    index = [i[0] for i in files_from_folders].index(file_in_folder)
                    if verbose:
                        eprint(f'Файл {files_from_folders[index][1]} замещён файлом {os.path.join(folder, file_in_folder)}')
                    files_from_folders.remove(files_from_folders[index])
                    files_from_folders.append([file_in_folder, os.path.join(folder, file_in_folder)])
        files_from_folders.sort(key=lambda x: x[0])
        files_from_folders = [i[1] for i in files_from_folders]
        # проверка на существование всех указанных корневых файлов
        files_to_recursive_scan = [i for i in files_to_recursive_scan if os.path.isfile(i)]
        if inversed_order:
            files = files_from_folders + files_to_recursive_scan
        else:
            files = files_to_recursive_scan + files_from_folders

    if required_extension:
        # фильтр по расширению
        files = [i for i in files if i.endswith(required_extension)]
    if verbose:
        eprint('Просматриваемый список файлов:')
        eprint(*files, sep='\n')
    return files
